package io.imp.orm.hibernate;

import java.util.Iterator;
import java.util.Set;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import io.imp.orm.Condition;

/**
 * @Description: TODO
 * @author CHEN-HF
 * @date 2017年12月14日 上午9:59:51
 * 
 */
public class JpaCondition implements Condition {

	public static int MAX_QUERY_JOIN_COUNT = 3;

	protected String property;
	protected String operator;
	protected Object value;

	public JpaCondition(String property, String operator, Object value) {
		this.property = property;
		this.operator = operator;
		this.value = value;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Predicate generateExpression(Root<?> root, CriteriaBuilder cb) {
		Expression exp = getExpression(root);
		if (this.value != null) {
			if (this.operator.equals("EQUALS")) {
				return cb.equal(exp, this.value);
			}
			if (this.operator.equals("NOT_EQUALS"))
				return cb.notEqual(exp, this.value);
			if (this.operator.equals("LIKE"))
				return cb.like(exp, "%" + this.value.toString() + "%");
			if (this.operator.equals("END"))
				return cb.like(exp, "%" + this.value.toString());
			if (this.operator.equals("START"))
				return cb.like(exp, this.value.toString() + "%");
			if (this.operator.equals("BETWEEN")) {
				String[] betweenArray = this.value.toString().split("-BTW-");
				if (betweenArray.length < 2) {
					return null;
				}
				return cb.between(exp, betweenArray[0], betweenArray[1]);
			}
			if (this.operator.equals("IN")) {
				if ((this.value instanceof Object[]))
					return exp.in((Object[]) (Object[]) this.value);
			} else if (this.operator.equals("NOT_IN")) {
				if ((this.value instanceof Object[]))
					cb.not(exp.in((Object[]) (Object[]) this.value));
			} else {
				if (this.operator.equals("LEFT")) {
					return cb.lessThan(exp, this.value.toString());
				}
				if (this.operator.equals("RIGHT")) {
					return cb.greaterThan(exp, this.value.toString());
				}
				if (this.operator.equals("LEFT_EQ")) {
					return cb.lessThanOrEqualTo(exp, this.value.toString());
				}
				if (this.operator.equals("RIGHT_EQ")) {
					return cb.greaterThanOrEqualTo(exp, this.value.toString());
				}
				if (this.operator.equals("IGNORE_CASE_EQUALS")) {
					return cb.equal(cb.upper(exp), this.value.toString());
				}
				if (this.operator.equals("IGNORE_CASE_LIKE")) {
					return cb.like(cb.upper(exp), "%" + this.value.toString() + "%");
				}
			}
		}
		if (this.operator.equals("IS_NULL"))
			return exp.isNull();
		if (this.operator.equals("NOT_NULL")) {
			return exp.isNotNull();
		}
		return null;
	}

	private Expression<?> getExpression(Root<?> root) {
		String propertyName = getProperty();
		//如果有.
		if (propertyName.indexOf(".") != -1) {
			int joinCount = propertyName.split("\\.").length;
			if (joinCount > MAX_QUERY_JOIN_COUNT) {
				// throw new BusException("查询参数[" + propertyName + "]关联层数不得大于 "
				// + MAX_QUERY_JOIN_COUNT);
			}
		}
		return getJoinExpression(root, propertyName);
	}

	@SuppressWarnings({ "rawtypes" })
	private Expression<?> getJoinExpression(From from, String propertyName) {
		Expression exp = null;
		if (propertyName.indexOf(".") != -1) {
			String alias = propertyName.split("\\.")[0];
			String proName = propertyName.substring(alias.length() + 1);
			Set joinSet = from.getJoins();
			Iterator i = joinSet.iterator();
			while (i.hasNext()) {
				Object next = i.next();
				if ((next instanceof Join)) {
					Join newJoin = (Join) next;
					if (alias.equals(newJoin.getAlias())) {
						exp = getJoinExpression(newJoin, proName);
						break;
					}
				}
			}

			if (exp == null) {
				Join newJoin = from.join(alias, JoinType.LEFT);
				from.alias(alias);
				exp = getJoinExpression(newJoin, proName);
			}
		} else {
			exp = from.get(propertyName);
		}
		return exp;
	}

	@Override
	public String getProperty() {
		return property;
	}

	@Override
	public String getOperator() {
		return operator;
	}

	@Override
	public Object getValue() {
		return value;
	}

}
